# Description
#    lzma is a general purpose data compression library https://tukaani.org/xz/
#    Public Domain

load("@bazel_skylib//lib:selects.bzl", "selects")
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
load("//:tools.bzl", "multi_test")

package(default_visibility = ["//visibility:private"])

config_setting(
    name = "osx_arm64",
    constraint_values = [
        "@platforms//os:osx",
        "@platforms//cpu:aarch64",
    ],
)

config_setting(
    name = "osx_x86_64",
    constraint_values = [
        "@platforms//os:osx",
        "@platforms//cpu:x86_64",
    ],
)

config_setting(
    name = "linux_arm64",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:aarch64",
    ],
)

config_setting(
    name = "linux_x86_64",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
)

expand_template(
    name = "config_linux_arm64",
    out = "config.lzma-linux-arm64.h",
    substitutions = {
        "{HAVE_IMMINTRIN_H}": "#undef HAVE_IMMINTRIN_H",
        "{HAVE_CPUID_H}": "#undef HAVE_CPUID_H",
        "{HAVE_USABLE_CLMUL}": "#undef HAVE_USABLE_CLMUL",
    },
    template = "config.lzma-linux.h",
)

expand_template(
    name = "config_linux_x86_64",
    out = "config.lzma-linux-x86_64.h",
    substitutions = {
        "{HAVE_IMMINTRIN_H}": """\
#if defined __has_include
  #if __has_include (<immintrin.h>)
    #define HAVE_IMMINTRIN_H 1
  #endif
#endif""",
        "{HAVE_CPUID_H}": "#define HAVE_CPUID_H 1",
        "{HAVE_USABLE_CLMUL}": "#define HAVE_USABLE_CLMUL 1",
    },
    template = "config.lzma-linux.h",
)

copy_file(
    name = "copy_config",
    src = selects.with_or({
        "@platforms//os:android": "config.lzma-android.h",
        ":linux_arm64": "config.lzma-linux-arm64.h",
        ":linux_x86_64": "config.lzma-linux-x86_64.h",
        ":osx_arm64": "config.lzma-osx-arm64.h",
        ":osx_x86_64": "config.lzma-osx-x86_64.h",
        ("@platforms//os:ios", "@platforms//os:watchos", "@platforms//os:tvos"): "apple_config",
        "@platforms//os:windows": "config.lzma-windows.h",
        "//conditions:default": "config.lzma-linux-x86_64.h",
    }),
    out = "src/liblzma/api/config.h",  # minimize the number of exported include paths
    target_compatible_with = selects.with_or({
        ("@platforms//os:android", "@platforms//os:linux", "@platforms//os:osx", "@platforms//os:ios", "@platforms//os:watchos", "@platforms//os:tvos", "@platforms//os:windows"): [],
        "//conditions:default": ["@platforms//:incompatible"],
    }),
)

# Configuration is the same across iOS, watchOS, and tvOS
alias(
    name = "apple_config",
    actual = select({
        "@platforms//cpu:arm64": "config.lzma-ios-arm64.h",
        "@platforms//cpu:armv7": "config.lzma-ios-armv7.h",
        "@platforms//cpu:x86_64": "config.lzma-osx-x86_64.h",  # Configuration same as macOS
        "@platforms//cpu:x86_32": "config.lzma-ios-i386.h",
    }),
)

# Note: lzma is bundled with Apple platforms, but sadly, not considered public API because its header is not exposed. lzma is not bundled on Android.

cc_library(
    name = "lzma",
    srcs = [
        "src/common/tuklib_cpucores.c",
        "src/common/tuklib_physmem.c",
    ] + glob(
        [
            "src/**/*.h",
            "src/liblzma/**/*.c",
        ],
        exclude = [
            "src/liblzma/check/crc*_small.c",
            "src/liblzma/**/*_tablegen.c",
        ],
    ),
    hdrs = [
        "src/liblzma/api/lzma.h",  # Publicly exported header
    ],
    copts = select({
        "@platforms//os:windows": [],
        "//conditions:default": ["-std=c99"],
    }),
    defines = select({
        "@platforms//os:windows": ["LZMA_API_STATIC"],
        "//conditions:default": [],
    }),
    linkopts = select({
        "@platforms//os:android": [],
        "@platforms//os:windows": [],
        "//conditions:default": ["-lpthread"],
    }),
    linkstatic = select({
        "@platforms//os:windows": True,
        "//conditions:default": False,
    }),
    strip_include_prefix = "src/liblzma/api",  # Allows public header without the path and without COPTS -I or includes = []
    visibility = ["//visibility:public"],
    deps = [
        "//:lzma_src_common",
        "//:lzma_src_liblzma_api",
        "//:lzma_src_liblzma_check",
        "//:lzma_src_liblzma_common",
        "//:lzma_src_liblzma_delta",
        "//:lzma_src_liblzma_lz",
        "//:lzma_src_liblzma_lzma",
        "//:lzma_src_liblzma_rangecoder",
        "//:lzma_src_liblzma_simpler",
    ],
)

cc_binary(
    name = "xz",
    srcs = glob(
        ["src/xz/*.c"],
        # exclude = ["src/xz/list.c"], # TODO Conditional this?
    ) + [
        "src/common/tuklib_mbstr_fw.c",
        "src/common/tuklib_mbstr_width.c",
        "src/common/tuklib_open_stdxxx.c",
    ],
    #  + select({
    #     "//conditions:COND_MAIN_DECODER": ["list.c"], # TODO Conditional this?
    #     "//conditions:default": [],
    # })
    copts = [
        '-DLOCALEDIR=\\"/usr/share/locale\\"',  #TODO Help? Where do we get the locale directory from in bazel?
    ],
    # linkopts = [
    # "$(CAPSICUM_LIB)",  #TODO Help?
    # "-llibintl",  # Replace with actual libintl target if available  #TODO Help?
    # ],
    visibility = ["//visibility:public"],
    deps = [
        "//:lzma",
        "//:lzma_src_common",
        "//:lzma_src_liblzma_api",
    ],
    #  + select({
    #     "@platforms//os:windows": ["//:xz_win_resources"],
    #     "//conditions:default": [],
    # }),
)

# cc_library(
#     name = "xz_win_resources",
#     srcs = ["xz_w32res.rc"],
# )

XZDEC_SRCS = [
    "src/xzdec/xzdec.c",
]

XZDEC_DEPS = [
    "//:lzma",
    "//:lzma_src_liblzma_common",
]
#  + select({
#     "@platforms//os:windows": ["//:xzdec_win_resources"],
#     "//conditions:default": [],
# })

# cc_library(
#     name = "xzdec_win_resources",
#     srcs = ["xzdec_w32res.rc"],
# )

cc_binary(
    name = "xzdec",
    srcs = XZDEC_SRCS,
    copts = [
        "-DTUKLIB_GETTEXT=0",
    ],
    visibility = ["//visibility:public"],
    # linkopts = ["-llibintl"],  # Replace with actual libintl target if available #TODO Help?
    deps = XZDEC_DEPS,
)

cc_binary(
    name = "lzmadec",
    srcs = XZDEC_SRCS,  # lzmadec uses the same source with a define
    copts = [
        "-DTUKLIB_GETTEXT=0",
        "-DLZMADEC",
    ],
    visibility = ["//visibility:public"],
    # linkopts = ["-llibintl"],  # Replace with actual libintl target if available #TODO Help?
    deps = XZDEC_DEPS,
)

cc_library(
    name = "lzma_src_common",
    srcs = [
        "src/common/tuklib_exit.c",
        "src/common/tuklib_progname.c",
    ],
    hdrs = glob(["src/common/*.h"]),
    defines = ["HAVE_CONFIG_H"] + select({
        "@platforms//os:windows": [
            "LZMA_API_STATIC",
            "TUKLIB_GETTEXT=0",  # Disable libintl for windows
        ],
        "//conditions:default": [],
    }),
    strip_include_prefix = "src/common",
    deps = [
        "//:lzma_src_liblzma_api",
    ],
)

cc_library(
    name = "lzma_src_liblzma_api",
    hdrs = [
        "src/liblzma/api/config.h",  # Generated above, so missed by glob. In srcs so it's not public like the other headers
    ] + glob(
        [
            "src/liblzma/api/**/*.h",
        ],
        exclude = [
            "src/liblzma/api/lzma.h",  # The public header, only used in hdrs of main lib (//visibility:public)
        ],
    ),
    strip_include_prefix = "src/liblzma/api",
)

cc_library(
    name = "lzma_src_liblzma_check",
    hdrs = glob(["src/liblzma/check/*.h"]),
    strip_include_prefix = "src/liblzma/check",
)

cc_library(
    name = "lzma_src_liblzma_common",
    hdrs = glob(["src/liblzma/common/*.h"]),
    includes = ["src/liblzma"],  # Needed as well as some usages use common/*.h instead of just the header
    strip_include_prefix = "src/liblzma/common",
)

cc_library(
    name = "lzma_src_liblzma_delta",
    hdrs = glob(["src/liblzma/delta/*.h"]),
    strip_include_prefix = "src/liblzma/delta",
)

cc_library(
    name = "lzma_src_liblzma_lz",
    hdrs = glob(["src/liblzma/lz/*.h"]),
    strip_include_prefix = "src/liblzma/lz",
)

cc_library(
    name = "lzma_src_liblzma_lzma",
    hdrs = glob(["src/liblzma/lzma/*.h"]),
    strip_include_prefix = "src/liblzma/lzma",
)

cc_library(
    name = "lzma_src_liblzma_rangecoder",
    hdrs = glob(["src/liblzma/rangecoder/*.h"]),
    strip_include_prefix = "src/liblzma/rangecoder",
)

cc_library(
    name = "lzma_src_liblzma_simpler",
    hdrs = glob(["src/liblzma/simple/*.h"]),
    strip_include_prefix = "src/liblzma/simple",
)

# Test header files
cc_library(
    name = "tests_hdrs",
    testonly = True,
    hdrs = [
        "tests/tests.h",
        "tests/tuktest.h",
    ],
)

# Suite of tests, much tidier using the custom macro from the tools.bzl
test_suite(
    name = "all_tests",
    tests = multi_test(
        size = "small",
        data = glob(["tests/files/**/*"]),
        defines = select({
            "@platforms//os:windows": ["HAVE_LZIP_DECODER=1"],
            "//conditions:default": [],
        }),
        names = [
            "bcj_test",
            "create_compress_files",
            "test_bcj_exact_size",
            "test_block_header",
            "test_check",
            "test_filter_flags",
            "test_filter_str",
            "test_hardware",
            "test_index",
            "test_index_hash",
            "test_lzip_decoder",
            "test_memlimit",
            "test_stream_flags",
            "test_vli",
        ],
        deps = [
            ":lzma",
            ":tests_hdrs",
        ],
    ) + [
        # "ossfuzz", # Currently disabled, as the fuzzer doesn't seem to be reliably available for linking on all systems. I don't have experience in this area :)
    ],
    visibility = ["//visibility:public"],
)

# Fuzz test, not currently implemented
cc_test(
    name = "ossfuzz",
    srcs = ["tests/ossfuzz/fuzz.c"],
    copts = [
        "-fsanitize=fuzzer",
    ],
    data = glob(["tests/files/**"]),
    linkopts = [
        "-fsanitize=fuzzer",
    ],
    deps = [
        ":tests_hdrs",
        "//:lzma",
    ],
)
